package j.framework.core.entity;

import j.util.New;
import j.util.StringUtils;
import j.util.TwoTuple;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 实体主体，此类里记录表的元数据信息、表字段的元数据、逻辑删除字段名称、主键（支持复合主键）、键
 * 这里将主键及约束键分开，方便处理
 * Created by junhai on 2015/1/6.
 */
public class Entity implements java.io.Serializable{

    /**
     * 自定义的执行触发器，key为何时执行
     */
    private Map<Integer,List<EntityInvokeTrigger>> invokeTriggers = New.map();

    /**
     * 获取何时执行所定义的触发器
     * @param whenInvokeKey
     * @return
     */
    public List<EntityInvokeTrigger> getInvokeTriggers(int whenInvokeKey){
        return invokeTriggers.get(whenInvokeKey);
    }

    /**
     * 增加一个触发器
     * @param trigger
     */
    public void addInvokeTrigger(EntityInvokeTrigger trigger){
        int whenInvoke = 1;
        List<EntityInvokeTrigger> list;
        //循环8次
        while(whenInvoke<=128){
            if((trigger.getWhenTriggerInvoke() & whenInvoke) == whenInvoke){
                list = invokeTriggers.get(trigger.getWhenTriggerInvoke());
                if(list == null){
                    list = New.list();
                    invokeTriggers.put(whenInvoke,list);
                }
                list.add(trigger);
            }
            whenInvoke*=2;
        }
    }

    public String getPrimaryKeys(){
        StringBuilder s = new StringBuilder();
        for(String key : primaryKeys){
            s.append(key).append(",");
        }
        if(s.length()>0){
            s.deleteCharAt(s.length()-1);
        }
        return s.toString();
    }

    /**
     * 增加人字段名
     */
    public static final String ADDED_USER_ID_FIELD_NAME = "added_user_id";
    /**
     * 增加时间字段名
     */
    public static final String ADDED_TIME_FIELD_NAME = "added_time";

    private String findByPkSql;
    private String logicDeletedSql;
    private String physicalDeletedSql;
    private List<String> findByPkArgsIndex;

    /**
     * 获取删除实体的语句执行计划，第一个参数为删除sql语句（参数为?），第二个为参数顺序
     * 若为逻辑删除，则修改删除标记为1
     * @return 第一个参数为物理删除sql语句，第二个参数为逻辑删除sql语句
     */
    public TwoTuple<String,String> getDeletedSql(){
        if(StringUtils.isNullOrEmpty(physicalDeletedSql)){
            synchronized (Entity.class){
                if(StringUtils.isNullOrEmpty(physicalDeletedSql)){
                    if(StringUtils.isNotEmpty(this.deletedFieldName)){
                        logicDeletedSql = "UPDATE "+this.getTableName()+" SET "+this.deletedFieldName+"=1 WHERE ";
                    }
                    physicalDeletedSql = "DELETE FROM "+this.getTableName()+" WHERE ";

                    //构造where语句
                    int i=0;
                    for(String key : findByPkArgsIndex){
                        if(i++>0){
                            logicDeletedSql += " AND ";
                            physicalDeletedSql += " AND ";
                        }
                        logicDeletedSql += key + " = ?";
                        physicalDeletedSql += key + " = ?";
                    }
                }
            }
        }
        return New.tuple(physicalDeletedSql, logicDeletedSql);
    }

    /**
     * 获取根据主键查询一个实体的sql语句
     * @return
     */
    public TwoTuple<String,List<String>> getFindByPkSql(){
        if(StringUtils.isNullOrEmpty(findByPkSql)){
            synchronized (Entity.class){
                if(StringUtils.isNullOrEmpty(findByPkSql)){
                    findByPkSql = "SELECT * FROM "+tableName+" WHERE ";
                    findByPkArgsIndex = New.list();
                    int i=0;
                    for(String key : primaryKeys){
                        if(i++>0){
                            findByPkSql+=" AND ";
                        }
                        findByPkSql += key+"=?";
                        findByPkArgsIndex.add(key);
                    }
                }
            }
        }
        return New.tuple(findByPkSql, findByPkArgsIndex);
    }
    private String insertedSql;//增加sql语句
    private List<String> insertedArgsIndex;


    /**
     * 获取并生成插入sql语句
     */
    public TwoTuple<String,List<String>> getInsertedSql(){
        if(StringUtils.isNullOrEmpty(insertedSql)){
            synchronized (Entity.class){
                if(StringUtils.isNullOrEmpty(insertedSql)){
                    insertedArgsIndex = New.list();
                    StringBuilder s = new StringBuilder("INSERT INTO ");
                    s.append(getTableName());
                    s.append("(");
                    //再将可插入的字段加入
                    for(String key : insertableFields){
                        s.append(key).append(",");
                        insertedArgsIndex.add(key);
                    }
                    s.deleteCharAt(s.length()-1);
                    s.append(")VALUES(");
                    //增加参数
                    for (int i = insertableFields.size() - 1; i >= 0; i--) {
                        s.append("?,");
                    }
                    s.deleteCharAt(s.length()-1);
                    s.append(")");
                    insertedSql = s.toString();
                }
            }
        }
        return New.tuple(insertedSql, insertedArgsIndex);
    }

    //权限相关
    private String functionId;

    private boolean isCheckFunctionPrivilege;//是否检测功能模块权限

    private String actionId;//操作权限

    private String insertActionId;

    private String updateActionId;

    private String deleteActionId;

    //增加时序列的key
    private String sequenceKey;

    //主键列表，对应的是数据表中的字段名称
    public final Set<String> primaryKeys = New.lset();

    private List<EntityKey> uniqueKeys;//唯一键

    //实体属性列表
    public final Map<String,EntityProperty> properties = New.map();

    //可增加的列,用一个有序的集合，可提供插入语句的执行计划
    public final Set<String> insertableFields = New.lset();
    //可修改的列
    public final Set<String> updatableFields = New.set();

    private String entityId;

    private String name;

    private String tableName;

    private String tag;  //由开发人员定义

    private String triggerClassName;

    private boolean isTreeTable;

    private String treeTableParentFieldName;

    private String treeTableLeftFieldName;

    private String treeTableRightFieldName;

    //若此表指定了逻辑删除标记，则记录在此属性中
    private String deletedFieldName;

    /**
     * 是否逻辑删除
     * @return true即逻辑删除
     */
    public boolean isLogicDeleted() {
        return StringUtils.isNotEmpty(deletedFieldName);
    }

    public String getSequenceKey() {
        return sequenceKey;
    }
    public void setSequenceKey(String sequenceKey) {
        this.sequenceKey = sequenceKey;
    }

    public String getEntityId() {
        return entityId;
    }
    public void setEntityId(String entityId) {
        this.entityId = entityId;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public String getTableName() {
        return tableName;
    }
    public void setTableName(String tableName) {
        this.tableName = tableName;
    }

    public String getTriggerClassName() {
        return triggerClassName;
    }
    public void setTriggerClassName(String triggerClassName) {
        this.triggerClassName = triggerClassName;
    }

    public String getTag() {
        return tag;
    }
    public void setTag(String tag) {
        this.tag = tag;
    }

    public boolean isTreeTable() {
        return isTreeTable;
    }
    public void setTreeTable(boolean treeTable) {
        this.isTreeTable = treeTable;
    }

    public String getTreeTableParentFieldName() {
        return treeTableParentFieldName;
    }
    public void setTreeTableParentFieldName(String treeTableParentFieldName) {
        this.treeTableParentFieldName = treeTableParentFieldName;
    }

    public String getTreeTableLeftFieldName() {
        return treeTableLeftFieldName;
    }
    public void setTreeTableLeftFieldName(String treeTableLeftFieldName) {
        this.treeTableLeftFieldName = treeTableLeftFieldName;
    }

    public String getTreeTableRightFieldName() {
        return treeTableRightFieldName;
    }
    public void setTreeTableRightFieldName(String treeTableRightFieldName) {
        this.treeTableRightFieldName = treeTableRightFieldName;
    }

    public String getDeletedFieldName() {
        return deletedFieldName;
    }
    public void setDeletedFieldName(String deletedFieldName) {
        this.deletedFieldName = deletedFieldName;
    }

    public List<EntityKey> getUniqueKeys() {
        return uniqueKeys;
    }
    public void setUniqueKeys(List<EntityKey> uniqueKeys) {
        this.uniqueKeys = uniqueKeys;
    }

    public String getFunctionId() {
        return functionId;
    }
    public void setFunctionId(String functionId) {
        this.functionId = functionId;
    }

    public boolean isCheckFunctionPrivilege() {
        return isCheckFunctionPrivilege;
    }
    public void setCheckFunctionPrivilege(boolean isCheckFunctionPrivilege) {
        this.isCheckFunctionPrivilege = isCheckFunctionPrivilege;
    }

    public String getActionId() {
        return actionId;
    }
    public void setActionId(String actionId) {
        this.actionId = actionId;
    }

    public String getInsertActionId() {
        return insertActionId;
    }
    public void setInsertActionId(String insertActionId) {
        this.insertActionId = insertActionId;
    }

    public String getUpdateActionId() {
        return updateActionId;
    }
    public void setUpdateActionId(String updateActionId) {
        this.updateActionId = updateActionId;
    }

    public String getDeleteActionId() {
        return deleteActionId;
    }
    public void setDeleteActionId(String deleteActionId) {
        this.deleteActionId = deleteActionId;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Entity entity = (Entity) o;

        if (entityId != null ? !entityId.equals(entity.entityId) : entity.entityId != null) return false;

        return true;
    }

    @Override
    public int hashCode() {
        return entityId != null ? entityId.hashCode() : 0;
    }
}
